package cn.com.yocat.flowable.config;

import org.flowable.common.engine.impl.runtime.Clock;
import org.flowable.engine.ProcessEngine;
import org.flowable.ui.common.properties.FlowableCommonAppProperties;
import org.flowable.ui.common.service.idm.RemoteIdmService;
import org.flowable.ui.common.service.idm.RemoteIdmServiceImpl;
import org.flowable.ui.common.service.idm.cache.RemoteIdmUserCache;
import org.flowable.ui.common.service.idm.cache.UserCache;
import org.flowable.ui.modeler.properties.FlowableModelerAppProperties;
import org.flowable.ui.modeler.repository.ModelHistoryRepository;
import org.flowable.ui.modeler.repository.ModelRelationRepository;
import org.flowable.ui.modeler.repository.ModelRepository;
import org.flowable.ui.modeler.service.FlowableModelQueryService;
import org.flowable.ui.modeler.service.ModelImageService;
import org.flowable.ui.modeler.service.ModelServiceImpl;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.flowable.ui.task.properties.FlowableTaskAppProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.task.TaskExecutorCustomizer;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.MultipartConfigElement;

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({FlowableModelerAppProperties.class, FlowableTaskAppProperties.class})
@ComponentScan(basePackages = {
        "org.flowable.ui.modeler.repository",
        "org.flowable.ui.modeler.serviceapi",
        "org.flowable.ui.modeler.service",
        "org.flowable.ui.common.repository",
        "org.flowable.ui.common.tenant",
        "org.flowable.ui.task.service",
        "org.flowable.ui.task.model.component"
}, excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ModelRelationRepository.class),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ModelRepository.class),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ModelHistoryRepository.class)
})
public class ApplicationConfiguration {
    @Bean
    public TaskExecutorCustomizer flowableTaskTaskExecutorCustomizer() {
        return (taskExecutor) -> {
            taskExecutor.setAwaitTerminationSeconds(30);
            taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        };
    }

    @Bean
    public ServletRegistrationBean modelerApiServlet(ApplicationContext applicationContext) {
        AnnotationConfigWebApplicationContext dispatcherServletConfiguration = new AnnotationConfigWebApplicationContext();
        dispatcherServletConfiguration.setParent(applicationContext);
        DispatcherServlet servlet = new DispatcherServlet(dispatcherServletConfiguration);
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(servlet, new String[]{"/api/editor/*"});
        registrationBean.setName("Flowable Modeler App API Servlet");
        registrationBean.setLoadOnStartup(1);
        registrationBean.setAsyncSupported(true);
        return registrationBean;
    }

    @Bean
    public ServletRegistrationBean<DispatcherServlet> modelerAppServlet(ApplicationContext applicationContext, ObjectProvider<MultipartConfigElement> multipartConfig) {
        AnnotationConfigWebApplicationContext dispatcherServletConfiguration = new AnnotationConfigWebApplicationContext();
        dispatcherServletConfiguration.setParent(applicationContext);
        dispatcherServletConfiguration.register(new Class[]{AppDispatcherServletConfiguration.class});
        DispatcherServlet servlet = new DispatcherServlet(dispatcherServletConfiguration);
        ServletRegistrationBean<DispatcherServlet> registrationBean = new ServletRegistrationBean(servlet, new String[]{"/modeler-app/*"});
        registrationBean.setName("Flowable Modeler App Servlet");
        registrationBean.setLoadOnStartup(1);
        registrationBean.setAsyncSupported(true);
        multipartConfig.ifAvailable(registrationBean::setMultipartConfig);
        return registrationBean;
    }

    @Bean
    public ModelService modelService() {
        return new ModelServiceImpl();
    }

    @Bean
    public FlowableModelQueryService flowableModelQueryService() {
        return new FlowableModelQueryService();
    }

    @Bean
    public ModelImageService modelImageService() {
        return new ModelImageService();
    }

    @Bean(name = {"clock"})
    @ConditionalOnMissingBean
    public Clock getClock(ProcessEngine processEngine) {
        return processEngine.getProcessEngineConfiguration().getClock();
    }

    @Bean
    public RemoteIdmService remoteIdmService(FlowableCommonAppProperties properties) {
        return new RemoteIdmServiceImpl(properties);
    }

    @Bean
    public UserCache remoteIdmUserCache(FlowableCommonAppProperties properties, RemoteIdmService remoteIdmService) {
        return new RemoteIdmUserCache(properties, remoteIdmService);
    }

}

